<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>字节场景题</title>
</head>
<body>
  <div>
    <span>
       <div></div>
       <div></div>
    </span>
    <span>
        <div></div>
        <div></div>
    </span>
  </div>
  <script>
    // 1. 输入一个字符数组， 转化为如下格式
    // input ["a","b","c","d","e","f","g"]
    // output {"a":{"b":{"c":{"d":{"e":{"f":"g"}}}}}}
    // function handler(arr){
    //   const len = arr.length
    //   let = prev = {
    //     [arr[len-2]]:arr[len-1]
    //   }
    //   for(let i = len - 3;i>=0;i--){
    //     prev = {
    //       [arr[i]]:prev
    //     }
    //   }
    //   return prev
    // }
    // input = ["a","b","c","d","e","f","g"]
    // console.log(handler(input))

    // 2. 查找json中的children路径
    // 现有如下json（简化为对象），已知每个节点id唯一，编写findNode(id)，返回路径，如findNode(5) 输出 1->4->5
    // {
    //   id: 1,
    //   children: [
    //     { id: 2, children: [{ id: 3, children: [] }] },
    //     {
    //       id: 4,
    //       children: [
    //         { id: 5, children: [] },
    //         { id: 6, children: [] },
    //       ],
    //     },
    //     { id: 7, children: [] },
    //   ],
    // };
    // function dfs(obj,path,target){
    //   if(obj.id === target){
    //     console.log(path.slice())
    //   }
    //   for(let a of obj.children){
    //     if(a !== []){
    //       path.push(a.id)
    //       dfs(a,path,target)
    //       path.pop()
    //     }
    //   }
    // }
    // function findNode(id){
    //   let json = {
    //   id: 1,
    //   children: [
    //     { id: 2, children: [{ id: 3, children: [] }] },
    //     {
    //       id: 4,
    //       children: [
    //         { id: 5, children: [] },
    //         { id: 6, children: [] },
    //       ],
    //     },
    //     { id: 7, children: [] },
    //   ],
    // };
    // let path = [json.id]
    // dfs(json,path,id)
    // }
    // findNode(5)

    // 3.如何获取页面中出现的所有dom元素的标签类型，得到一个数组，数组的每一项是一个标签名

    // 其实只要知道如何获取页面中所有元素就能做出来, 通过document.querySelectorAll("*")获取
    // const all = Array.from(document.querySelectorAll("*"));
    // const hash = {};
    // const res = [];
    // all.forEach(it=>{
    //     if(!hash[it.tagName]){
    //         res.push(it.tagName);
    //         hash[it.tagName] = true;
    //     }
    // })
    // console.log(res)

    // 4. 实现retry，方法接受两个入参：
    // promiseFunction：调用promiseFunction产生一个promise实例，实例有可能成功，有可能失败
    // timeLimit：timeslimit是最大可重试次数
    // promises实例状态扭转为成功之后，函数返回的promise实例扭转为成功，失败之后，会检查当前充实次数是否超过限制，如果没有超过的话则重试，超过的话，则将返回的promise实例扭转为失败

    // function retry(promiseFunction,timesLimit){}

    // 使用方法：
    // retry(promise,3).then(res => {}).catch(err => {})


    // 随机生成不同状态的promise
    // function randomPromise() {
    //     return new Promise((resolve, reject) => {
    //         let num = ~~(Math.random() * 10)
    //         console.log(num);
    //         if (num % 2) {
    //             resolve('ok')
    //         } else {
    //             reject('err')
    //         }
    //     })
    // }

    // function retry(promiseFunction, timesLimit) {
    //     let res = [],count = timesLimit
    //     return new Promise((resolve, reject) => {
    //         while(count--){
    //             Promise.resolve(promiseFunction()).then(
    //                 res => {
    //                     resolve(res)
    //                 },
    //                 err => {
    //                     res.push(err)
    //                     if(timesLimit === res.length) reject(res)
    //                 }
    //             )
    //         }
    //     })
    // }

    // retry(randomPromise, 3)
    //     .then(res => {
    //         console.log(res);
    //     }).catch(err => {
    //         console.log(err);
    //     })

    // 5. 版本号比较以及其优化
    // function compare( version1 ,  version2 ) {
    //   //先对字符串进行分隔
    //   const v1 = version1.split(".");
    //   const v2 = version2.split(".");
    //   const len = Math.max(v1.length,v2.length);
    //   //依次比较
    //   for(let i=0; i<len; i++){
    //     //将字符串转化为数字
    //     const n1 = Number(v1[i] || 0);
    //     const n2 = Number(v2[i] || 0);
    //     if(n1 > n2) return 1;
    //     if(n1 < n2) return -1;
    //   }
    //   return 0;
    // }
    // console.log(compare('1.1','1.1.1'))

    // 6. 版本号排序
    // function arrSort(arr) {
    //   arr.sort((version1, version2) => {
    //     //先对字符串进行分隔
    //     const v1 = version1.split(".");
    //     const v2 = version2.split(".");
    //     const len = Math.max(v1.length, v2.length);
    //     //依次比较
    //     for (let i = 0; i < len; i++) {
    //       //将字符串转化为数字
    //       const n1 = Number(v1[i] || 0);
    //       const n2 = Number(v2[i] || 0);
    //       if (n1 > n2) return 1;
    //       if (n1 < n2) return -1;
    //     }
    //     return 0;
    //   })
    // }
 
    // const arr = ['4.8', '1.7.1', '4.1.9', '5'];
    // arrSort(arr);
    // console.log(arr); //["1.7.1", "4.1.9", "4.8", "5"]

    // 7.找出dom节点下所有标签类型；
    // let elementCollection = document.getElementsByTagName("*")
    // let elementArray = Array.from(elementCollection)
    // let tagArray = elementArray.map(item => item.tagName)
    // let noRepeatTagArray = Array.from(new Set(tagArray))
    // console.log(noRepeatTagArray)

    // 8.深度遍历 dom，打印遍历路径（dfs)
    // let printInfo = (node) => {console.log(node.tagName, '.' + node.className)}
    // function dfs (node) {
    //   printInfo(node)
    //   if(node.children.length){
    //       Array.from(node.children).forEach(function (el) {
    //           dfs(el)
    //       })
    //   }
    // }
    // dfs(root)

    // 9.实现发布订阅模式
    // class EventBus {
    //   constructor() {
    //     this.eventList = {}
    //   }

    //   // 订阅(注册事件)
    //   on(eventName, callback) {
    //     let taskQueue = this.eventList[eventName]
    //     if (!taskQueue) {
    //       taskQueue = []
    //     }
    //     taskQueue.push(callback)
    //   }

    //   // 触发事件
    //   emit(eventName, ...args) {
    //     let taskQueue = this.eventList[eventName]
    //     if (taskQueue && taskQueue.length > 0) {
    //       taskQueue.forEach((callback) => {
    //         callback(...args)
    //       })
    //     }
    //   }

    //   // 取消订阅
    //   remove(eventName, callback) {
    //     let taskQueue = this.eventList[eventName]
    //     if (taskQueue) {
    //       taskQueue = taskQueue.filter(fn=>{
    //         fn != callback
    //       })
    //     }
    //   }

    //   // 触发1次
    //   once(eventName, callback){
    //     const fn = ()=>{
    //       callback()
    //       this.remove(eventName, fn)
    //     }
    //     this.on(eventName, fn)
    //   }
    // }

    // 10.求和函数：假设有一个add函数，接受任意数量的数字作为输入，返回一个函数；该函数也能接收数量的数组作为输入，输出结果为前后输入的所有数字之和。
    // function myAdd(){
    //   let arr = new Array()
    //   arr = [...arguments]
    //   return function(){
    //     let sum = arr.concat([...arguments]).reduce((prev,now)=>prev+now)
    //     return sum
    //   }
    // }
    // let add_1_and_2 = myAdd(1,2)
    // console.log(add_1_and_2(3,4))

    // 11.实现函数solution(arr, k)
    // arr是number数组，k是number，返回前k个最小的数字组成的数组，保持相对顺序
    // 输入:[1,2,3,4,5,3,2]，3，输出：[1,2,2]
    // 输入:[1,2,3,4,5,3,2]，4，输出：[1,2,3,2]
    // 输入:[1,2,3,4,5,3,2]，5，输出：[1,2,3,3,2]
    // function solution(arr,k){
    //   let hash = new Map()
    //   arr.forEach((element,index) => {
    //     if(hash.size < k){
    //       hash.set(index,element)
    //     }
    //     for(let i = 0; i< hash.size;i++){
    //       if(hash.get(i) > element){
    //         hash.delete(i)
    //         hash.set(index,element)
    //         break
    //       }
    //     }        
    //   });
    //   return Array.from(hash.values())
    // }
    // console.log(solution([1,2,3,4,5,3,2],5)) 

    // 12.并发Promise，一次并发6次，如果中途reject一次，整体函数返回Promise.reject。整体成功返回成功的结果列表。
    // function PromiseCount(promises,count){
    //   let promisesLength = promises.length;
    //   let promisesCount = 0
    //   let promiseRes = [];
    //   if(count > promisesLength) return;
    //   return new Promise((resolve,reject) =>{
    //     for(let i = 0;i < promisesLength && promisesCount<count;i++){
    //       promises[i].then(res =>{
    //         promiseRes[i] = res;
    //         promisesCount++;
    //       },err =>{
    //         reject(err)
    //       })
    //     }
    //     resolve(promiseRes)
    //   })
    // }

    // 13.手写实现EventBus
    // class EventBus{
    //   constructor(){
    //     this.handlers = {}
    //   }
    //   on(fnName,fn){
    //     if(!this.handlers[fnName]){
    //       this.handlers[fnName] = [];
    //     }
    //     this.handlers[fnName].push(fn);
    //   }
    //   trigger(fnName){
    //     const args = [...arguments].splice(1)
    //     const fnList = this.handlers[fnName];
    //     if(!fnList || fnList.length === 0){
    //       return false;
    //     }else{
    //       fnList.forEach(element => {
    //         element.apply(this,args);            
    //       });
    //     }
    //   }
    //   off(fnName){
    //     const fnList = this.handlers[fnName];
    //     if(fnList && fnList !== 0){
    //       delete this.handlers[fnName]
    //     }
    //   }
    //   once(fnName,fn){
    //     let onceFn = (...args) => {
    //       fn.apply(this,args);
    //       this.off(fnName);
    //     }
    //     this.on(fnName,onceFn);
    //   }
    // }
    // // 开始测试
    // const a=(param)=>{console.log(param)} 
    // let bus=new EventBus() 
    // bus.on('event1',a) 
    // bus.trigger('event1',1) 
    // bus.trigger('event1',2) 
    // bus.off('event1') 
    // bus.trigger('event1',3) 
    // bus.once('event2',a) 
    // bus.trigger('event2',4) 
    // bus.trigger('event2',5) // 1 // 2 // 4 

    // 14. 给一个DOM树（真实DOM），输出它JSON格式的对象
    // <div>
    //   <span>
    //     <div></div>
    //     <div></div>
    //   </span>
    //   <span>
    //       <div></div>
    //       <div></div>
    //   </span>
    // </div>
    // {
    // tag:'DIV',
    // children:[
    //     {tag:'SPAN'
    //       children:[{tag:'DIV',
    //             children:[]},

    //             {tag:'DIV',
    //             children:[]}
    //                     ]},

    //       {tag:'SPAN'
    //       children:[{tag:'DIV',
    //             children:[]},

    //           {tag:'DIV',
    //             children:[]}
    //           ]}
    //     ]
    // }
    // function convertToJson() {
    //   const root = document.getElementsByTagName('body')[0].children
    //   const output = new Object();
    //   output.tag = root[0].tagName;
    //   output.children = getChilds(root[0]);
    //   console.log(JSON.stringify(output));
    // }

    // function getChilds(node) {
    //   const children = node.children;
    //   const result = new Array();
    //   if(!children || children.length === 0) return result;
    //   for (const child of children) {
    //     const childOutput = new Object();
    //     childOutput.tag = child.tagName;
    //     childOutput.children = getChilds(child);
    //     result.push(childOutput);
    //   }
    //   return result;
    // }
    // convertToJson();

    // 15. 给n个异步操作，要求存入队列中然后按序执行。
    // 异步函数a
    // var a = function () {  
    //   return new Promise(function (resolve, reject) {    
    //     setTimeout(function () {      
    //       resolve('a')    
    //     }, 1000)  
    //   }) 
    // }
    // 异步函数b
    // var b = function (data) {  
    //   return new Promise(function (resolve, reject) {    
    //     resolve(data + 'b')  
    //   }) 
    // }
    // 异步函数c
    // var c = function (data) {  
    //   return new Promise(function (resolve, reject) {    
    //     setTimeout(function () {      
    //       resolve(data + 'c')    
    //     }, 500)  
    //   }) 
    // }
    // 方法一 链式调用
    // a().then((data) => {
    //   return b(data)
    // }).then((data) => {
    //   return c(data)
    // }).then((data) => {
    //   console.log(data)
    // })

    // 方法二 构建队列
    // function queue(arr){
    //   var sequence = Promise.resolve()
    //   arr.forEach(item => {
    //     sequence = sequence.then(item)
    //   });
    //   return sequence
    // }
    // queue([a, b, c]).then(data => {
    //   console.log(data)
    // })

    // async和await构建队列
    // async function queue(arr) {  
    //   let res = null  
    //   for (let promise of arr) {    
    //     res = await promise(res)  
    //   }  
    //   return await res 
    // } 
    // queue([a, b, c]).then(data => {    
    //   console.log(data)
    // })


    // 16. 处理高并发, 100 条数据，带宽为 10， 跑满带宽
    // 模拟一百条数据 
    const message = new Array(100).fill(''); 
    for (let i = 0; i < 100; i++) {  
      message[i] = '第' + i + '条数据'; 
    } // 模拟请求一千条数据

    function axiosGet(idx) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(message[idx]);
        }, 1000 * Math.random());
      })
    }


    async function asyncProcess(max = 10) {
      const task = [];
      const ans = [];
      for (let i = 0; i < 100; i++) {
        const p = axiosGet(i).then(res => {
          console.log(res, task.length);
          ans.push(res);
          task.splice(task.indexOf(p), 1);
        });
        task.push(p);
        if (task.length === max) {
          await Promise.race(task);
        }
      }
      await Promise.allSettled(task);
      return ans;
    }
    asyncProcess().then(res=>{
    console.log(res);})

    // 17.settimeout(() => {console.log(1)},1000)怎么让1在2s后打印，不能直接修改时间
      // const p = new Promise ((resolve,reject) =>{
      //   console.log(Date().toString())
      //   setTimeout(() => {
      //     resolve(1)
      //   },1000)
      // })
      // p.then(res =>{
  
      //   setTimeout(()=>{
      //     console.log(Date().toString())
      //     console.log(res)
      //   },1000)
        
      // })
    // 18.实例链式调用：如let a = new Man(); a.sleep(3000).sayHi().sleep(1000).sleep(2000).sayHi()；写出Man()构造函数
    // class Man {
    //   constructor(){
    //     this.queue = []; // 记录调用的函数,全部是同步收集然后依次执行
    //     this.index = 0; // 执行函数的索引
    //     this.init();
    //   }

    //    // 因为执行需要在函数收集之前，因此需要利用事件机制，先收集后执行
    //   init() {
    //     setTimeout(() => this.run(),0);
    //   }

    //   run(){
    //     const fn = this.queue[this.index++]
    //     // 最后一个 fn 为 undefined, 因此需要做处理
    //     fn && fn();
    //   }

    //   sleep(delay){
    //     this.queue.push(() => {
    //       setTimeout(() =>{
    //         this.run()
    //       },delay)
    //     });
    //     return this;
    //   }

    //   sayHi(){
    //     this.queue.push(()=>{
    //       console.log('Hi')
    //       this.run()
    //     });
    //     return this
    //   }
    // }
    // let a = new Man(); 
    // a.sleep(3000).sayHi().sleep(1000).sleep(2000).sayHi();
    
    // 19.数组拍平
    // function _flat(arr,depth){
    //   let res = []
    //   if(!Array.isArray(arr) || depth <= 0) return arr
    //   arr.forEach(item => {
    //     if(Array.isArray(item) && depth > 0){
    //       res = res.concat(arguments.callee(item,depth-1))
    //     }
    //     else{
    //       res.push(item)
    //     }
    //   });
    //   return res
    // }
    // console.log(_flat([[1,2,3],1,2,3,[[[5]]]],3))
    
    // Array.prototype._flat = function(){
    //   let res = []

    //   this.forEach(item => {
    //     if(Array.isArray(item)){
    //       res = res.concat(item._flat())
    //     }
    //     else{
    //       res.push(item)
    //     }
    //   });
    //   return res
    // }
    // console.log([[1,2,3],1,2,3,[[[5]]]]._flat()) 

    

  </script>
</body>
</html>